package org.boot.wxipad.support.api.impl;

import lombok.extern.slf4j.Slf4j;
import org.boot.wxipad.common.error.WxIpadError;
import org.boot.wxipad.common.error.WxIpadErrorException;
import org.boot.wxipad.common.util.http.RequestExecutor;
import org.boot.wxipad.common.util.http.RequestHttp;
import org.boot.wxipad.common.util.http.SimpleFormRequestExecutor;
import org.boot.wxipad.common.util.http.SimpleGetRequestExecutor;
import org.boot.wxipad.common.util.http.SimplePostRequestExecutor;
import org.boot.wxipad.support.config.WxIpadConfigStorage;
import org.boot.wxipad.support.api.WxIpadFriendService;
import org.boot.wxipad.support.api.WxIpadService;

import java.io.IOException;

/**
 * @author liyuncong
 * @version 1.0
 * @file BaseWxIpadServiceImpl
 * @brief BaseWxIpadServiceImpl
 * @details BaseWxIpadServiceImpl
 * @date 2024-08-05
 *
 * Edit History
 * ----------------------------------------------------------------------------
 * DATE                     NAME               DESCRIPTION
 * 2024-08-05               liyuncong          Created
 */

@Slf4j
public abstract class BaseWxIpadServiceImpl<H, P> implements WxIpadService, RequestHttp<H, P> {
    private WxIpadFriendService friendService = new WxIpadFriendServiceImpl(this);


    protected WxIpadConfigStorage configStorage;

    private int retrySleepMillis = 1000;
    //    private int maxRetryTimes = 5;
    private int maxRetryTimes = 2;

    @Override
    public String get(String url, String queryParam) throws WxIpadErrorException {
        return this.execute(SimpleGetRequestExecutor.create(this), url, queryParam);
    }

    @Override
    public String post(String url, String postData) throws WxIpadErrorException {
        return this.execute(SimplePostRequestExecutor.create(this), url, postData);
    }

    @Override
    public String postForm(String url, String formData) throws WxIpadErrorException {
        return this.execute(SimpleFormRequestExecutor.create(this), url, formData);
    }

    @Override
    public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxIpadErrorException {
        int retryTimes = 0;
        do {
            try {
                return this.executeInternal(executor, uri, data);
            } catch (WxIpadErrorException ex) {
                if (retryTimes + 1 > this.maxRetryTimes) {
                    log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
                    //最后一次重试失败后，直接抛出异常，不再等待
                    throw new WxIpadErrorException("IPAD服务端异常，超出重试次数");
                }

                WxIpadError error = ex.getError();
                // 等待1000ms后重试
                int sleepMillis = this.retrySleepMillis * (1 << retryTimes);
                try {
                    log.debug("IPAD服务繁忙，{} ms 后重试(第{}次)", sleepMillis, retryTimes + 1);
                    Thread.sleep(sleepMillis);
                } catch (InterruptedException exception) {
                    Thread.currentThread().interrupt();
                }
            }
        } while (retryTimes++ < this.maxRetryTimes);

        log.warn("重试达到最大次数【{}】", this.maxRetryTimes);
        throw new WxIpadErrorException("IPAD服务端异常，超出重试次数.");
    }

    protected <T, E> T executeInternal(RequestExecutor<T, E> executor, String uri, E data) throws WxIpadErrorException {
        try {
            T result = executor.execute(uri, data);
            log.debug("\n【请求地址】: {}\n【请求参数】：{}\n【响应数据】：{}", uri, data, result);
            return result;
//        } catch (WxIpadErrorException exception) {
//            WxIpadError error = exception.getError();
//            if (error.getStatus() != 200) {
//                log.error("\n【请求地址】: {}\n【请求参数】：{}\n【错误信息】：{}", uri, data, error);
//                throw new WxIpadErrorException(error, exception);
//            }
//            return null;
        } catch (IOException exception) {
            log.error("\n【请求地址】: {}\n【请求参数】：{}\n【异常信息】：{}", uri, data, exception.getMessage());
            throw new WxIpadErrorException(exception);
        }
    }


    @Override
    public WxIpadConfigStorage getWxIpadConfigStorage() {
        return this.configStorage;
    }

    @Override
    public void setWxIpadConfigStorage(WxIpadConfigStorage configStorage) {
        this.configStorage = configStorage;
        this.initHttp();
    }

    @Override
    public WxIpadFriendService getFriendService() {
        return friendService;
    }
}
